﻿#pragma once

#include "../util/box_std_allocator.hh"
#include <ctime>
#include <functional>
#include <string>

namespace kratos {
namespace service {

class ScriptDebugger;

using BreakID = std::string;

/**
 * @brief 断点状态
 */
enum class BreakPointState {
  ENABLE = 1, ///< 可用
  DISABLE,    ///< 禁用
};
/**
 * @brief 断点
 */
struct BreakPoint {
  std::string file;                               ///< 文件名
  int line{0};                                    ///< 行号
  BreakPointState state{BreakPointState::ENABLE}; ///< 断点状态
};

using DebuggerCallback = std::function<void(const ScriptDebugger &)>;
using BreakpointMap = kratos::service::PoolUnorederedMap<BreakID, BreakPoint>;

struct StackBase {
  virtual ~StackBase() {}
  virtual auto to_string() const -> std::string = 0;
  virtual auto upvalues() const -> std::string = 0;
  virtual auto locals() const -> std::string = 0;
  virtual auto file_path() const -> const std::string & = 0;
  virtual auto bp_line() const -> int = 0;
  virtual auto empty() const -> bool = 0;
};

class ScriptDebugger {
public:
  virtual ~ScriptDebugger() {}
  /**
   * @brief 主循环
   * @param ms 当前时间戳
   * @return
   */
  virtual auto update(std::time_t ms) -> void = 0;
  /**
   * @brief 设置源代码根目录
   * @param source_root 源代码根目录
   * @return
   */
  virtual auto set_source_root(const std::string &source_root) -> void = 0;
  /**
   * @brief 获取源代码根目录
   * @return 源代码根目录
   */
  virtual auto get_source_root() -> const std::string & = 0;
  /**
   * @brief 设置调试器名称
   * @param name 调试器名称
   * @return
   */
  virtual auto set_name(const std::string &name) -> void = 0;
  /**
   * @brief 获取调试器名称
   * @return 调试器名称
   */
  virtual auto get_name() const -> const std::string & = 0;
  /**
   * @brief 禁止调试器
   */
  virtual auto disable() -> void = 0;
  /**
   * @brief 调试器生效
   */
  virtual auto enable() -> void = 0;
  /**
   * @brief 是否被禁用
   * @return true或false
   */
  virtual auto is_enable() -> bool = 0;
  /**
   * @brief 添加一批断点
   * @param breakpoints 断点
   * @param all_on 是否全部强制开启
   * @return
   */
  virtual auto add_breakpoint(const BreakpointMap &breakpoints, bool all_on)
      -> bool = 0;
  /**
   * @brief 添加断点
   *
   * @param file 文件名
   * @param line 行号
   * @return 断点ID
   */
  virtual auto add_breakpoint(const std::string &file, int line) -> BreakID = 0;
  /**
   * @brief 删除断点
   * @param break_id 断点ID
   * @return
   */
  virtual auto remove_breakpoint(BreakID break_id) -> void = 0;
  /**
   * @brief 开启断点
   * @param break_id 断点ID
   * @return
   */
  virtual auto enable_breakpoint(BreakID break_id) -> void = 0;
  /**
   * @brief 禁用断点
   * @param break_id 断点ID
   * @return
   */
  virtual auto disable_breakpoint(BreakID break_id) -> void = 0;
  /**
   * @brief 开始执行, 遇到断点挂起
   * @param error 错误描述
   * @return true或false
   */
  virtual auto execute(std::string &error) -> bool = 0;
  /**
   * @brief step in, 达成条件后挂起
   * @param error 错误描述
   * @return true或false
   */
  virtual auto step_in(std::string &error) -> bool = 0;
  /**
   * @brief step out, 达成条件后挂起
   * @param error 错误描述
   * @return true或false
   */
  virtual auto step_out(std::string &error) -> bool = 0;
  /**
   * @brief step over, 达成条件后挂起
   * @param error 错误描述
   * @return true或false
   */
  virtual auto step_over(std::string &error) -> bool = 0;
  /**
   * @brief 获取所有断点
   * @return 所有断点表
   */
  virtual auto get_all_breakpoint() const -> const BreakpointMap & = 0;
  /**
   * @brief 设置事件回调
   * @param cb 事件回调
   * @return
   */
  virtual auto set_cb(DebuggerCallback cb) -> void = 0;
  /**
   * @brief 获取事件回调
   * @return 事件回调
   */
  virtual auto get_cb() -> DebuggerCallback = 0;
  /**
   * @brief 是否挂起虚拟机
   * @return true或false
   */
  virtual auto is_suspend() -> bool = 0;
  /**
   * @brief 获取当前栈
   * @return 当前栈
   */
  virtual auto get_stack() const -> const StackBase & = 0;
  /**
   * @brief 打印变量值
   * @param name 变量名
   * @param [OUT] value 变量值
   * @return true或false
   */
  virtual auto print(const std::string &name, std::string &value) -> bool = 0;
  /**
   * @brief 执行一段代码
   * @param code 代码
   * @param [OUT] 执行结果
   * @return true或false
   */
  virtual auto eval(const std::string &code, std::string &result) -> bool = 0;
  /**
   * @brief 获取正在被调试的虚拟机的唯一ID
   * @return 正在被调试的虚拟机的唯一ID
   */
  virtual auto get_unique_id() const -> std::uint64_t = 0;
  /**
   * @brief 获取backtrace
   * @param level 栈深度, -1表示全部
   * @param [OUT] backtrace 获取的信息
   * @return true或false
   */
  virtual auto get_backtrace(int level, std::string &backtrace) const
      -> bool = 0;
  /**
   * @brief 选择某层调用栈
   * @param level 调用栈层次
   * @return true或false
   */
  virtual auto frame(int level) -> bool = 0;
  /**
   * @brief 获取脚本引擎版本信息
   * @return 脚本引擎版本信息
  */
  virtual auto get_machine_version() -> std::string = 0;
};

} // namespace service
} // namespace kratos
